﻿/*
    Copyright 2013 Dmitry Bratus

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

function cdTest() {
    var result = {
        initialIsRoot: cd() == '/'
    };

    result.returnsSelf = cd('/Processors') == '/Processors';
    result.pathIsMapped = globalPath('P1/P2') == '/Processors/P1/P2';
    result.globalIsGlobal = globalPath('/Global/Path') == '/Global/Path';
    result.idIsId = globalPath('#1') == '#1';

    return result;
}

function getTest() {
    cd('/Roles');

    var adminRole = get('Administrator');
    var adminRoleInfo = getInfo('Administrator');

    return {
        get: !!adminRole,
        getPayload: !!adminRole.users,
        getInfo: !!adminRoleInfo
    };
}

function dirTest() {
    var atRoot3 = dir('/', {
        offset: 0,
        max: 3
    });

    cd('/Roles');

    var roles1 = dir('', { max: 1 });

    $dir.save('/Workflows', new Workflow({ name: 'W2', definitionType: 'T1' }));
    $dir.save('/Workflows', new Workflow({ name: 'W1', definitionType: 'T2' }));
    $dir.save('/Workflows', new Workflow({ name: 'W3', definitionType: 'T3' }));

    cd('/');

    var workflowsAsc = dir('/Workflows', { sortBy: SORT_BY_NAME, sort: ASC });
    var noWorkflows = dir('/Workflows', { entryType: DET_FOLDER });
    var allWorkflows = dir('/Workflows', { entryType: DET_WORKFLOW });
    var allWorkflowsWithPayload = dir('/Workflows', { entryType: DET_WORKFLOW, withPayload: true });

    return {
        atRoot3: atRoot3.length == 3,
        roles1: roles1.length == 1,
        workflowsAsc: 
            workflowsAsc.length == 3 && 
            workflowsAsc[0].name == 'W1' &&
            workflowsAsc[1].name == 'W2' &&
            workflowsAsc[2].name == 'W3',
        noWorkflows: noWorkflows.length == 0,
        allWorkflows: allWorkflows.length == 3,
        allWorkflowsWithPayload:
            !!(allWorkflowsWithPayload.length == 3 &&
            allWorkflowsWithPayload[0].definitionType &&
            allWorkflowsWithPayload[1].definitionType &&
            allWorkflowsWithPayload[2].definitionType)
    };
}

function countMkdirRmRenameTest() {
    cd('/Sets/Shared');
    mkdir('Dir1');

    var dirCreated = exists('/Sets/Shared/Dir1');

    rename('Dir1', 'Dir2');

    var dirRenamed = exists('/Sets/Shared/Dir2');

    cd('/Sets');

    var count1 = !!(count('Shared', DET_FOLDER) == 1);

    mkdir('Shared/Dir1');
    mkdir('Shared/Dir3');

    var count3 = !!(count('Shared', DET_FOLDER) == 3);
    var count0 = !!(count('Shared', DET_WORKFLOW) == 0);

    rm('Shared/Dir3');
    
    var count2 = !!(count('Shared', DET_FOLDER) == 2);

    return {
        dirCreated: dirCreated,
        dirRenamed: dirRenamed,
        count0: count0,
        count1: count1,
        count2: count2,
        count3: count3
    };
}

function grantRevokeTest() {
    cd('/Sets/Shared');

    mkdir('Restricted');

    var initialPermissions = !!(get('Restricted').acl.length == 0);

    grant({
        on: 'Restricted',
        to: ['Application'],
        permission: READ
    });
    grant({
        on: 'Restricted',
        to: ['Processor', 'Application'],
        permission: WRITE
    });

    var acl = get('Restricted').acl;

    if (!acl) {
        throw new Error('/Sets/Shared/Restricted has no ACL.');
    }
    
    var permissions2 =
        acl.length == 2 &&
        _(acl).some(function (p) { return p.role == 'Application' && (p.permission & (READ | WRITE)); }) &&
        _(acl).some(function (p) { return p.role == 'Processor' && (p.permission & WRITE); });

    revoke({
        on: 'Restricted',
        from: ['Application'],
        permission: READ
    });
    revoke({
        on: 'Restricted',
        from: ['Processor'],
        permission: WRITE
    });

    acl = get('Restricted').acl;

    var permissions1 =
        acl.length == 1 &&
        _(acl).some(function (p) { return p.role == 'Application' && (p.permission & WRITE); }) &&
        !_(acl).some(function (p) { return p.role == 'Application' && (p.permission & READ); }) &&
        !_(acl).some(function (p) { return p.role == 'Processor' && (p.permission & WRITE); });
        
    return {
        initialPermissions: initialPermissions,
        permissions1: permissions1,
        permissions2: permissions2
    };
}

function lnTest() {
    cd('/Sets');

    ln('Shared/Ln', 'Local');

    var targetId = get('Local').id;
    var byPath = get('Shared/Ln').target == targetId;

    rm('Shared/Ln');

    ln('Shared/Ln', targetId);

    var byId = get('Shared/Ln').target == targetId;

    return {
        byPath: byPath,
        byId: byId
    };
}

function createProcessorTest() {
    createOrUpdateProcessor({
        name: 'TestProcessor',
        role: 'Processor/TestProcessor',
        allowSchedulingTo: ['Application/App1', 'Application/App2']
    });

    var proc = get('/Processors/TestProcessor');
    var pending = get('/Processors/TestProcessor/Pending');
    var running = get('/Processors/TestProcessor/Running');
    var waiting = get('/Processors/TestProcessor/Waiting');
    var suspended = get('/Processors/TestProcessor/Suspended');
    var broken = get('/Processors/TestProcessor/Broken');
    var finished = get('/Processors/TestProcessor/Finished');

    return {
        created: !!(proc),
        pending: !!(pending),
        running: !!(running),
        waiting: !!(waiting),
        suspended: !!(suspended),
        broken: !!(broken),
        finished: !!(finished),
        processorAcl:
            !!(proc) &&
            _(proc.acl).some(function (p) { return p.role == 'Application/App1' && (p.permission & READ); }) &&
            _(proc.acl).some(function (p) { return p.role == 'Application/App2' && (p.permission & READ); }) &&
            _(proc.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & READ); }),
        pendingAcl:
            !!(pending) &&
            _(pending.acl).some(function (p) { return p.role == 'Application/App1' && (p.permission & (READ | CREATE_CHILDREN)); }) &&
            _(pending.acl).some(function (p) { return p.role == 'Application/App2' && (p.permission & (READ | CREATE_CHILDREN)); }) &&
            _(pending.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        runningAcl:
            !!(running) &&
            _(running.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        waitingAcl:
            !!(waiting) &&
            _(waiting.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        suspendedAcl:
            !!(suspended) &&
            _(suspended.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        brokenAcl:
            !!(broken) &&
            _(broken.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        finishedAcl:
            !!(finished) &&
            _(finished.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); })
    };
}

function updateProcessorTest() {
    createOrUpdateProcessor({
        name: 'TestProcessor',
        role: 'Processor/TestProcessor1',
        allowSchedulingTo: ['Application/App3', 'Application/App4']
    });
    
    createOrUpdateProcessor({
        name: 'TestProcessor',
        role: 'Processor/TestProcessor',
        allowSchedulingTo: ['Application/App1', 'Application/App2']
    });

    var proc = get('/Processors/TestProcessor');
    var pending = get('/Processors/TestProcessor/Pending');
    var running = get('/Processors/TestProcessor/Running');
    var waiting = get('/Processors/TestProcessor/Waiting');
    var suspended = get('/Processors/TestProcessor/Suspended');
    var broken = get('/Processors/TestProcessor/Broken');
    var finished = get('/Processors/TestProcessor/Finished');

    return {
        created: !!(proc),
        pending: !!(pending),
        running: !!(running),
        waiting: !!(waiting),
        suspended: !!(suspended),
        broken: !!(broken),
        finished: !!(finished),
        processorAcl:
            !!(proc) &&
            _(proc.acl).some(function (p) { return p.role == 'Application/App1' && (p.permission & READ); }) &&
            _(proc.acl).some(function (p) { return p.role == 'Application/App2' && (p.permission & READ); }) &&
            _(proc.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & READ); }),
        pendingAcl:
            !!(pending) &&
            _(pending.acl).some(function (p) { return p.role == 'Application/App1' && (p.permission & (READ | CREATE_CHILDREN)); }) &&
            _(pending.acl).some(function (p) { return p.role == 'Application/App2' && (p.permission & (READ | CREATE_CHILDREN)); }) &&
            _(pending.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        runningAcl:
            !!(running) &&
            _(running.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        waitingAcl:
            !!(waiting) &&
            _(waiting.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        suspendedAcl:
            !!(suspended) &&
            _(suspended.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        brokenAcl:
            !!(broken) &&
            _(broken.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); }),
        finishedAcl:
            !!(finished) &&
            _(finished.acl).some(function (p) { return p.role == 'Processor/TestProcessor' && (p.permission & (READ | CREATE_CHILDREN)); })
    };
}

function createUpdateRoleTest() {
    createOrUpdateRole({
        name: 'Processor/MyProcessor',
        users: [ 'user1', 'user2' ]
    });

    var users = usersOfRole('Processor/MyProcessor');
    var create = _(users).contains('user1') && _(users).contains('user2');
    
    createOrUpdateRole({
        name: 'Processor/MyProcessor',
        users: ['user3', 'user4']
    });

    users = usersOfRole('Processor/MyProcessor');
    var update = _(users).contains('user3') && _(users).contains('user4');

    addUsersToRole('Processor/MyProcessor', ['user5', 'user6']);
    users = usersOfRole('Processor/MyProcessor');
    
    var add = 
        _(users).contains('user3') && _(users).contains('user4') &&
        _(users).contains('user5') && _(users).contains('user6');

    removeUsersFromRole('Processor/MyProcessor', ['user5', 'user6']);
    users = usersOfRole('Processor/MyProcessor');
    
    var remove =
        _(users).contains('user3') && _(users).contains('user4') &&
        !_(users).contains('user5') && !_(users).contains('user6');

    return {
        create: create,
        update: update,
        add: add,
        remove: remove
    };
}

function createUpdateSetTest() {
    cd('/Sets');

    createOrUpdateSet({
        at: 'Shared/TestSet',
        messageType: 'MessageType',
        capacity: 1
    });

    var set = get('/Sets/Shared/TestSet');

    var create = !!(set);
    var createMessageType = create && set.messageType == 'MessageType';
    var createCapacity = create && set.capacity == 1;

    createOrUpdateSet({
        at: 'Shared/TestSet',
        messageType: 'NewMessageType',
        capacity: 2
    });

    set = get('/Sets/Shared/TestSet');

    var updateMessageType = create && set.messageType == 'NewMessageType';
    var updateCapacity = create && set.capacity == 2;

    return {
        create: create,
        createMessageType: createMessageType,
        createCapacity: createCapacity,
        updateMessageType: updateMessageType,
        updateCapacity: updateCapacity
    };
}

function createUpdateWorkflowTest() {
    createOrUpdateWorkflow({
        name: 'TestWorkflow',
        definitionType: 'Workflow1',
        preferredProcessors: [ 'Proc1', 'Proc2' ]
    });

    var wf = get('/Workflows/TestWorkflow');

    var create = !!(wf);
    var createDefinitionType = create && wf.definitionType == 'Workflow1';
    var createPreferredProcessors =
        create && _(wf.preferredProcessors).contains('Proc1') && _(wf.preferredProcessors).contains('Proc2');
    
    createOrUpdateWorkflow({
        name: 'TestWorkflow',
        definitionType: 'Workflow2',
        preferredProcessors: ['Proc3', 'Proc4']
    });

    wf = get('/Workflows/TestWorkflow');
    
    var updateDefinitionType = create && wf.definitionType == 'Workflow2';
    var updatePreferredProcessors =
        create && _(wf.preferredProcessors).contains('Proc3') && _(wf.preferredProcessors).contains('Proc4');

    return {
        create: create,
        createDefinitionType: createDefinitionType,
        createPreferredProcessors: createPreferredProcessors,
        updateDefinitionType: updateDefinitionType,
        updatePreferredProcessors: updatePreferredProcessors
    };
}

function retryProcessTest() {
    createOrUpdateProcessor({
        name: 'TestProcessor'
    });

    var pid = $dir.save('/Processors/TestProcessor/Broken', new Process({ workflow: 'Workflow' }));

    retryProcess(pid);

    return {
        retried: exists('/Processors/TestProcessor/Pending/' + pid)
    };
}